/*
 * Created on 12 juin 08 by Joseph Poidevin (poidevin@gmail.com)
 */
package org.bigcrunch.processing.libcv;

import java.lang.reflect.Method;
import java.util.LinkedList;
import java.util.List;

import processing.core.PApplet;
import processing.core.PConstants;
import processing.core.PImage;
import toxi.video.capture.IncompatibleSizeException;
import toxi.video.capture.LibCV;
import toxi.video.capture.SimpleCapture;

public class LibCVExt extends LibCV implements PConstants{
    protected List regions;
    private PApplet parent;
    private float threshold = 0.05f;
    private PImage debugView;
    
    private final static String METHOD_NAME = "processRegionEvent";
    private Method processEventMethod;
    
    protected boolean debug = true;
    
    public LibCVExt(PApplet a, SimpleCapture c) {
        super(a, c);
        this.parent = a;
        regions = new LinkedList();
        parent.registerPost(this);
        parent.registerDispose(this);
        parent.registerPre(this);
        
     // check to see if the host applet implements
        try {
            processEventMethod = parent.getClass().getMethod(METHOD_NAME,
                                        new Class[] { Region.class });
        } catch (Exception e) {
            PApplet.println("applet must implement "+METHOD_NAME+"(Region) method");
        }
    }
    
    public void pre(){
        
        PImage di;
        PImage frame = getFrame();
        try {
            processFrame(frame);
            
            for(int i = 0;i<regions.size();i++){
                Region r = (Region)regions.get(i);
                di = getDeltaBuffer().get(r.getX(),r.getY(),r.getWidth(),r.getHeight());
               
             // compute an average change factor of the current delta buffer
                float sum=0;
                for(int k=0; k<di.pixels.length; k++) sum+=di.pixels[k] & 0xff;
                // flash red, if there's more than 5% change
                if (sum/di.pixels.length/256 > threshold) {
                  r.indicator=255;
                  fireEvent(r);
                } 
                else {
                    //di = frame.get(r.getX(),r.getY(),r.getWidth(),r.getHeight());
                  r.indicator*=0.9;
                }
                //di = frame.get(r.getX(),r.getY(),r.getWidth(),r.getHeight());
                //di.filter(PImage.POSTERIZE,Math.max(20,r.indicator));//,Math.min(r.indicator,20)
                if(debug){
                    if(debugView==null){
                        debugView = new PImage(frame.width,frame.height);
                        debugView.format = PImage.RGB;
                    }
                  for(int j=0; j<di.pixels.length; j++) 
                      di.pixels[j] = parent.color(r.indicator,r.indicator,r.indicator);
                  debugView.set(r.getX(), r.getY(), di);  
                }
            }
            
        } catch (IncompatibleSizeException e) {
            e.printStackTrace();
        }
    }
    
    public void post(){
        if(debug && debugView!=null){
            int tw = debugView.width/2;
            int th = debugView.height/2;
            parent.g.copy(debugView,0, 0,debugView.width,debugView.height,0,0,tw,th);
            parent.noFill();
            parent.stroke(255);
            parent.rect(0, 0, tw, th);
        }
    }
    
    public void addRegion(Region region){
        regions.add(region);
    }
    
    public void setGrid(int gridSize){
        int count = 0;
        int w = capture.getWidth()/gridSize;
        int h = capture.getHeight()/gridSize;
        for(int i = 0;i<gridSize;i++){
            for(int j = 0;j<gridSize;j++){
                Region r = new Region(i*w,j*h,w,h);
                r.setId(count++);
                addRegion(r);
            }
        }
    }
    
    protected void fireEvent(Region region){
        if (processEventMethod != null) {
            //System.out.println(region);
            try {
                processEventMethod.invoke(parent, new Object[] { region });
            } catch (Exception e) {
                PApplet.println("Disabling "+METHOD_NAME+"() because of an error.");
                e.printStackTrace();
                processEventMethod = null;
            }
        }
    }
    
    public boolean isDebug() {
        return debug;
    }

    public void setDebug(boolean debug) {
        this.debug = debug;
    }

    public float getThreshold() {
        return threshold;
    }

    public void setThreshold(float threshold) {
        this.threshold = threshold;
    }
    
    public PImage getDebugBuffer(){return this.debugView;}
    
    // call by PApplet on close
    public void dispose(){
        stop();
        if(capture!=null)
            capture.shutdown();
    }

}
